#include "OpenGLTexture2D.h"
#include "Core/Base.h"
#include "Debug/Instrumentor.h"


namespace Hazel {

    OpenGLTexture2D::OpenGLTexture2D(const std::string &path) :m_Path(path){
        HZ_PROFILE_FUNCTION();
        int width,height,channels;

        stbi_set_flip_vertically_on_load(1);
//        stbi_uc* data = stbi_load(path.c_str(),&width,&height,&channels,0);
        stbi_uc* data = nullptr;
        {
            HZ_PROFILE_SCOPE("stbi_load - OpenGLTexture2D::OpenGLTexture2D(const std:string&)");
            data = stbi_load(path.c_str(), &width, &height, &channels, 0);
        }
        HZ_CORE_ASSERT(data,"Failed to load image!");

        m_width=width;
        m_height = height;

        GLint internalFormat = 0;
        GLenum dataFormat = 0;
        if (channels == 4) {
            internalFormat = GL_RGBA8;
            dataFormat = GL_RGBA;
        } else if (channels == 3) {
            internalFormat = GL_RGB8;
            dataFormat = GL_RGB;
        }
        HZ_CORE_ASSERT(internalFormat && dataFormat, "Format not supported!");
        /*
         * OpenGL提供了GLUtils.textImage2D()方法来实现以上效果，但是在调用此方法之前可以设置
            纹理过滤函数 glTexParameterf()
            glTexParmeterf()函数来确定如何把图象从纹理图象空间映射到帧缓冲图象空间(如：映射时为了避免多边形上的图像失真，而重新构造纹理图像等)。

            GL_TEXTURE_2D: 操作2D纹理.
        　　GL_TEXTURE_WRAP_S: S/T方向上的贴图模式.
        　　GL_CLAMP: 将纹理坐标限制在0.0,1.0的范围之内.如果超出了会如何呢.不会错误,只是会边缘拉伸填充.
            GL_LINEAR: 线性过滤, 使用距离当前渲染像素中心最近的4个纹素加权平均值.
            GL_TEXTURE_MAG_FILTER: 放大过滤
            GL_TEXTURE_MIN_FILTER: 缩小过滤
            GL_LINEAR_MIPMAP_NEAREST: 使用GL_NEAREST对最接近当前多边形的解析度的两个层级贴图进行采样,然后用这两个值进行线性插值
         * */
        glGenTextures(1, &m_RendererID);
        glBindTexture(GL_TEXTURE_2D, m_RendererID);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

        glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, dataFormat, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);



        stbi_image_free(data);
    }

    OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height)
            : m_width(width), m_height(height)
    {
        HZ_PROFILE_FUNCTION();
        m_InternalFormat = GL_RGBA8;
        m_DataFormat = GL_RGBA;
        glGenTextures(1, &m_RendererID);

        glBindTexture(GL_TEXTURE_2D, m_RendererID);
        glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, width, height, 0, m_DataFormat, GL_UNSIGNED_BYTE, nullptr);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    }


    OpenGLTexture2D::~OpenGLTexture2D() {
        HZ_PROFILE_FUNCTION();
        glDeleteTextures(1, &m_RendererID);
    }

    void OpenGLTexture2D::Bind(uint32_t slot) const {
        HZ_PROFILE_FUNCTION();
        glActiveTexture(GL_TEXTURE0 + slot);
        glBindTexture(GL_TEXTURE_2D, m_RendererID);

        // OpenGL4.5+
        // glBindTextureUnit(slot, m_RendererID);
    }

    // 设置纹理数据，使用glTexSubImage2D
    void OpenGLTexture2D::SetData(void *data, uint32_t size) {
        HZ_PROFILE_FUNCTION();
        uint32_t bpp = m_DataFormat == GL_RGBA ? 4 : 3;
        HZ_CORE_ASSERT(size == m_width * m_height * bpp, "Data must be entire");

        glBindTexture(GL_TEXTURE_2D, m_RendererID);
        /*
         * glTexSubImage2D 这个方法功能是替换纹理
         * level 指定详细级别编号。 0级是基本图像级别。 级别n是第n个mipmap缩小图像。
         * xoffset 指定纹理数组中x方向的像素偏移量，x方向是指纹理坐标，即左下角为（0，0）。
           yoffset 指定纹理数组中y方向的纹素偏移，y方向是指纹理坐标，即左下角为（0，0）。
           format 指定像素数据的格式。 接受以下符号值：GL_ALPHA，GL_RGB，GL_RGBA，GL_LUMINANCE和GL_LUMINANCE_ALPHA。
           type 指定像素数据的数据类型。 接受以下符号值：GL_UNSIGNED_BYTE，GL_UNSIGNED_SHORT_5_6_5，GL_UNSIGNED_SHORT_4_4_4_4和GL_UNSIGNED_SHORT_5_5_5_1。
           data 指定指向内存中图像数据的指针

         * */
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width,
                        m_height, m_DataFormat, GL_UNSIGNED_BYTE, data);
    }
}